"""
====================================================================
健康数据分析模块 / Health Data Analysis Module
====================================================================
功能说明：
1. 提供员工健康数据分析的WebSocket接口
2. 调用AI生成健康分析报告和建议
3. 支持用户token认证

作者: Data Analysis Team
日期: 2025-01-06
====================================================================
"""

import json
import numpy as np
import pandas as pd
from fastapi import APIRouter
from starlette.websockets import WebSocket, WebSocketState
from dao.es_dao.p_health_log_dao import get_resting_hr_P20_by_imei_day, get_night_avg_hr_spo2_by_imei_day, \
    get_day_avg_hr_by_imei_day, get_hr_timeseries_by_imei, get_hr_timeseries_by_imei_page
from utils.mongo_util import db

ai_router = APIRouter(prefix="/dataAnalysis/ai", tags=["人工智能接口"])


@ai_router.websocket("/healthAnalysisWs")
async def websocket_endpoint(ws: WebSocket):
    """
    WebSocket端点：员工健康数据分析

    功能：
    1. 验证用户token并获取权限
    2. 检查分析结果缓存
    3. 获取健康数据（心率、血氧、步数）
    4. 计算六维健康评分
    5. 调用AI生成健康分析报告
    6. 缓存分析结果

    请求参数（JSON格式）：
    {
        "user_id": <用户ID>,
        "start_time": "2024-01-01T00:00:00Z",
        "end_time": "2024-01-31T23:59:59Z"
    }

    响应格式：
    - type: "analysis_data" - 健康分析数据
    - type: "analysis_stream" - AI分析流式内容
    - type: "analysis_stream_done" - AI分析完成
    - type: "error" - 错误信息
    """
    # ====================================================================
    # 步骤1: 用户认证
    # ====================================================================
    # from middleware.auth_middleware import authenticate_websocket_token
    #
    # user_context = await authenticate_websocket_token(ws)

    # if not user_context:
    #     # 认证失败，发送错误消息并关闭连接
    #     try:
    #         await ws.send_json({
    #             "type": "error",
    #             "code": "AUTH_FAILED",
    #             "message": "认证失败：token无效或已过期，请重新登录"
    #         })
    #     except:
    #         pass
    #
    #     await ws.close(code=1008)  # 1008 = Policy Violation (认证失败)
    #     return

    # 认证成功，接受WebSocket连接
    user_context = {}
    await ws.accept()

    try:
        while True:
            # ====================================================================
            # 步骤2: 接收并验证请求参数
            # ====================================================================
            # 接收前端参数
            raw_data = await ws.receive_text()
            data: dict = json.loads(raw_data)

            # ====================================================================
            # 步骤3: 检查分析结果缓存
            # ====================================================================
            # 判断是否有分析记录（基于memberId和时间范围）
            param = {
                "memberId": data['term']['member.id'],
                "time_range": {
                    "start": data["start_time"],
                    "end": data["end_time"]
                }
            }
            analysis_history = db.health_analysis.find_one(param)
            if analysis_history:
                # 找到缓存结果，直接返回
                del analysis_history['_id']  # 移除MongoDB的_id字段
                await ws.send_json(analysis_history)
                continue

            # ====================================================================
            # 步骤4: 获取健康数据
            # ====================================================================
            # 获取用户权限（部门列表）

            query_bool = {"bool": {"filter": [{"terms": {"dataAuthority.depart": [data['term']['member.id']]}}]}}
            # 获取日间工作静息心率衍生数据
            df_hr_day, summary_hr_day = await get_summary_hr_cs(data, query_bool)

            # 获取昼夜节律数据
            df_day_and_night = await get_day_and_night(data, query_bool)

            # ====================================================================
            # 步骤6: 检测运动恢复心率与疲劳指数
            # ====================================================================
            # recovery_df = await detect_recovery_hr_and_fatigue(
            #     query_bool,
            #     start_time=data['start_time'],
            #     end_time=data['end_time']
            # )

            # ====================================================================
            # 步骤7: 计算六维评分
            # ====================================================================
            # 心血管稳定性评分
            df_cs = analyze_health_period(df_hr_day) if not df_hr_day.empty else pd.DataFrame()

            # 供氧效率评分
            df_oe = calculate_oxygen_efficiency(df_day_and_night) if not df_day_and_night.empty else pd.DataFrame()

            # 昼夜节律评分
            df_cs2 = calculate_circadian_rhythm(df_day_and_night) if not df_day_and_night.empty else pd.DataFrame()

            df_summary = await run_health_analysis(df_day_and_night) if not df_day_and_night.empty else pd.DataFrame()

            # ====================================================================
            # 步骤9: 构建分析数据载荷
            # ====================================================================
            analysis_payload = _build_health_analysis_payload(
                request_payload=data,
                df_summary=df_summary
            )

            # 发送分析数据给前端
            await ws.send_json(analysis_payload)

            # ====================================================================
            # 步骤10: 调用AI生成分析报告
            # ====================================================================
            # 导入AI客户端
            from client.openai_client import invoke_laozhang_api

            # 构建AI查询prompt
            ai_query = gen_health_query({
                'summary': df_summary.to_csv(sep='|', index=False) if not df_summary.empty else ""
                # 'recovery_data': recovery_df.to_csv(sep='|', index=False) if not recovery_df.empty else ""
            })

            # 调用LaoZhang API进行流式分析
            model = "gpt-4o"
            system_prompt = (
                "你是企业员工健康管理顾问。目标：基于六维健康评分（心血管稳定性/供氧效率/代谢效率/恢复疲劳/心理压力/昼夜节律），"
                "识别健康风险、定位可能原因、提供健康干预建议，帮助提升员工身体和心理健康水平。"
            )

            ai_result = await invoke_laozhang_api(
                ai_query,
                ws,
                system_prompt=system_prompt,
                model=model
            )

            # ====================================================================
            # 步骤11: 保存分析结果到数据库
            # ====================================================================
            # 添加AI结果和元数据
            analysis_payload["ai_result"] = ai_result
            analysis_payload["user_id"] = data['term']['member.id']
            analysis_payload["timestamp"] = pd.Timestamp.now().isoformat()

            # 保存到MongoDB
            try:
                db.health_analysis.insert_one(analysis_payload)
                print(f"✅ 健康分析结果已保存到数据库 (memberId: {data['term']['member.id']})")
            except Exception as e:
                print(f"⚠️ 保存分析结果失败: {str(e)}")

    except Exception as e:
        # 业务异常时，若连接还活着，发送错误信息
        print(f"处理WebSocket请求时发生错误: {str(e)}")

        if ws.application_state != WebSocketState.DISCONNECTED:
            try:
                await ws.send_json({
                    "type": "error",
                    "code": "INTERNAL_ERROR",
                    "message": f"服务器内部错误: {str(e)}"
                })
                await ws.close(code=1011)  # 1011 = Internal Server Error
            except RuntimeError:
                pass

    finally:
        # 最终清理：只在仍未断开的情况下再关一次
        if ws.application_state != WebSocketState.DISCONNECTED:
            try:
                await ws.close()
            except RuntimeError:
                pass


async def get_day_and_night(data, query_bool):
    o2 = await get_night_avg_hr_spo2_by_imei_day(
        query_bool, start_time=data['start_time'],
        end_time=data['end_time'])
    df_night_hr_o2 = pd.DataFrame(o2)
    # 获取白天平均心率
    hr_avg_day = await get_day_avg_hr_by_imei_day(
        query_bool, start_time=data['start_time'],
        end_time=data['end_time'])
    df_hr_avg_day = pd.DataFrame(hr_avg_day)
    # ====================================================================
    # 步骤5: 数据处理和昼夜对比
    # ====================================================================
    # 合并昼夜数据
    df_day_and_night = df_hr_avg_day.merge(
        df_night_hr_o2[["imei", "date", "avg_hr", "avg_spo2", "spo2_ratio_lt95"]],
        on=["imei", "date"],
        how="left",
        suffixes=("_day", "_night")
    )
    df_day_and_night["hr_day_night_ratio"] = (
        (df_day_and_night["avg_hr_day"] / df_day_and_night["avg_hr_night"])
            .replace([float('inf'), float('-inf')], float('nan'))
    ).round(2)
    # 保留两位小数
    cols_to_round = ["avg_hr_day", "avg_hr_night", "avg_spo2", "hr_day_night_ratio"]
    df_day_and_night[cols_to_round] = df_day_and_night[cols_to_round].round(2)
    return df_day_and_night


async def get_summary_hr_cs(data, query_bool):

    hr_day_data = await get_resting_hr_P20_by_imei_day(query_bool, start_time=data['start_time'],
                                                       end_time=data['end_time'])
    df_hr_day = pd.DataFrame(hr_day_data)
    # 这里CS为心血管稳定性，计算逻辑。满分100.
    # 日间静息心率 p20 > 60 每多1扣1分；2. p20与p80之间的标准差大于 > 10，每多1扣1.5分

    return df_hr_day


# -*- coding: utf-8 -*-
"""
health_analysis.py

输入：包含 imei / date / P20 / std_p10_P80 的日级 DataFrame
输出：按 imei 聚合后的健康分析结果 DataFrame
"""


# 跑一个脚本。查出 es 表里的数据{

# {
#         "_index" : "p_health_logs",
#         "_type" : "_doc",
#         "_id" : "e68LXZoB7ehN4j9CzEAe",
#         "_score" : null,
#         "_source" : {
#           "createDate" : "2025-11-07T14:40:45.000Z",
#           "dataType" : "血氧",
#           "imei" : "861265062521056",
#           "healthValue" : 98,
#           "dataAuthority" : {
#             "depart" : [
#               1938,
#               102152,
#               102258,
#               102298
#             ]
#           },
#           "intensity" : 0,
#           "step_8_min" : 0,
#           "exception_level" : null
#         },
#         "sort" : [
#           1762526445000
#         ]
#       }
# 这个数据格式下。在按时间顺序连续2条或3条，运动强度 intensity 大于2的情况下。直到后面一条数据的心率的 intensity 小于2。 则他的心率就是运动恢复心率。
# 当数据连续停止时。比如3条之后没有数据了。就查着最后一条的时间后面两分钟之内有没有采集到心率。我要计算他的恢复疲劳指数。虽然这个已经过了几分钟了。但聊胜于无。


def _compute_trend(x_days: np.ndarray, y_values: np.ndarray) -> float:
    """
    用最小二乘拟合斜率，表示趋势（每天变化多少）
    x_days: 从0开始的天数
    y_values: 对应的指标（P20 或 std）
    """
    if len(x_days) < 3:
        return 0.0
    # 如果全是同一个值，没趋势
    if np.allclose(y_values, y_values[0], equal_nan=True):
        return 0.0
    # 去掉 NaN
    mask = ~np.isnan(y_values)
    if mask.sum() < 3:
        return 0.0
    x = x_days[mask]
    y = y_values[mask]
    # polyfit 一阶多项式的系数 [k, b]，k 为斜率
    k, _ = np.polyfit(x, y, 1)
    return float(k)


def _compute_cs(P20_mean: float,
                std_mean: float,
                std_std: float,
                trend_P20: float) -> float:
    """
    计算一个时间窗口（例如一月/一周）的心血管稳定性综合评分 CS_month（0~100）

    这里的参数（HR_opt, SD_opt, a1, a2 等）可以根据实际人群情况微调。
    """
    # 理想值 & 灵敏度参数（可以根据后续样本调参）
    HR_opt = 60.0  # 理想静息心率附近
    SD_opt = 10.0  # 理想波动水平
    a1 = 2.0  # 静息心率偏高的惩罚系数
    a2 = 1.2  # 波动偏高的惩罚系数

    # 任意一个关键量缺失则返回 NaN
    if np.isnan(P20_mean) or np.isnan(std_mean) or np.isnan(std_std):
        return float("nan")

    # 静息心率越高，扣分越多
    cs_hr = 100.0 - a1 * (P20_mean - HR_opt)

    # 波动越大，扣分越多
    cs_sd = 100.0 - a2 * (std_mean - SD_opt)

    # 天与天之间波动越大，说明状态不稳定，也扣分
    cs_stability = 100.0 - std_std

    # 趋势：P20 越往上走，说明静息心率在升高（体能变差），给负向权重
    trend_penalty = -10.0 * trend_P20  # 1 bpm/月 ≈ -10 分，可按实际调整

    # 综合权重（可调）
    w_hr = 0.4
    w_sd = 0.3
    w_stability = 0.2
    w_trend = 0.1

    cs = w_hr * cs_hr + w_sd * cs_sd + w_stability * cs_stability + w_trend * trend_penalty

    # 限定在 0~100 之间
    cs = max(0.0, min(100.0, cs))
    return float(cs)


async def run_health_analysis(df: pd.DataFrame) -> pd.DataFrame:
    """
    执行健康数据分析函数，对输入的DataFrame进行分组聚合计算，生成员工健康汇总表。

    参数:
        df: pandas.DataFrame，包含员工健康数据的DataFrame
           预期包含字段：imei, date, avg_hr, avg_spo2, spo2_ratio_lt95等

    返回:
        pandas.DataFrame: 包含各项健康指标汇总的表

    对标 equipment.py 的 run_data_analysis()
    """
    if df.empty:
        return pd.DataFrame()

    # 按imei聚合健康指标
    summary = df.groupby(['imei'], as_index=False).agg(
        valid_days=('imei', 'size'),  # 有效数据天数
        avg_hr_day=('avg_hr_day', 'mean'),  # 白天平均心率
        avg_hr_night=('avg_hr_night', 'mean'),  # 夜间平均心率
        avg_spo2=('avg_spo2', 'mean'),  # 平均血氧
        max_spo2=('avg_spo2', 'max'),  # 最高血氧
        min_spo2=('avg_spo2', 'min'),  # 最低血氧
        spo2_ratio_lt95=('spo2_ratio_lt95', 'mean'),  # 低氧比例
        hr_day_night_ratio=('hr_day_night_ratio', 'mean'),  # 昼夜心率比值
    )

    # 计算衍生指标
    summary['hr_range'] = summary['avg_hr_day'] - summary['avg_hr_night']
    summary['spo2_stability'] = (summary['max_spo2'] - summary['min_spo2']).round(2)
    summary['hr_level'] = summary['avg_hr_day'].apply(
        lambda x: '正常' if 60 <= x <= 100 else ('偏低' if x < 60 else '偏高')
    )
    summary['spo2_level'] = summary['avg_spo2'].apply(
        lambda x: '优秀' if x >= 97 else ('良好' if x >= 95 else '需改善')
    )

    return summary.round(2)


def _build_weekly_health_summary(df_result: pd.DataFrame) -> tuple:
    """
    汇总周度健康指标统计，便于后续在周粒度展示达标统计。
    对标 equipment.py 的 _build_weekly_summary()

    参数:
        df_result: 包含日级健康数据的DataFrame

    返回:
        tuple: (weekly_records_list, weekly_records_csv)
    """
    if df_result.empty:
        return [], ""

    df_week = df_result.copy()
    df_week['date'] = pd.to_datetime(df_week['date'], errors='coerce')

    # 按周聚合
    df_week['week_period'] = df_week['date'].dt.to_period("W-SUN")
    weekly = df_week.groupby(['imei', 'week_period'], as_index=False).agg(
        avg_hr_day=('avg_hr_day', 'mean'),
        avg_hr_night=('avg_hr_night', 'mean'),
        avg_spo2=('avg_spo2', 'mean'),
        spo2_ratio_lt95=('spo2_ratio_lt95', 'mean'),
        days_count=('date', 'nunique')
    ).round(2)

    weekly['week_period'] = weekly['week_period'].astype(str)

    # 转为JSON和CSV格式
    weekly_json = json.loads(weekly.to_json(orient='records', force_ascii=False))
    weekly_csv = weekly.to_csv(sep='|', index=False)

    return weekly_json, weekly_csv


def _aggregate_health_scores(df_health_scores: pd.DataFrame) -> tuple:
    """
    生成部门/项目维度的平均健康评分。
    对标 equipment.py 的 _aggregate_scores()

    参数:
        df_health_scores: 包含 imei, department, group, 各维评分 的DataFrame

    返回:
        tuple: (department_scores, group_scores, department_csv, group_csv, score_sort_json)
    """
    if df_health_scores.empty:
        return [], [], "", "", {}

    # 按部门聚合（若有部门字段）
    if 'department' in df_health_scores.columns:
        df_dept = df_health_scores.groupby('department', as_index=False).mean(numeric_only=True)
        dept_scores_json = json.loads(df_dept.to_json(orient='records', force_ascii=False))
        dept_scores_csv = df_dept.to_csv(sep='|', index=False)
    else:
        dept_scores_json = []
        dept_scores_csv = ""

    # 按分组聚合（若有分组字段）
    if 'group' in df_health_scores.columns:
        df_group = df_health_scores.groupby('group', as_index=False).mean(numeric_only=True)
        group_scores_json = json.loads(df_group.to_json(orient='records', force_ascii=False))
        group_scores_csv = df_group.to_csv(sep='|', index=False)
    else:
        group_scores_json = []
        group_scores_csv = ""

    # 生成排序和Top/Bottom数据
    if 'overall_score' in df_health_scores.columns:
        df_top3 = df_health_scores.nlargest(3, 'overall_score')
        df_bottom3 = df_health_scores.nsmallest(3, 'overall_score')
    else:
        # 用平均评分代替
        df_health_scores['avg_score'] = df_health_scores.select_dtypes(include=[np.number]).mean(axis=1)
        df_top3 = df_health_scores.nlargest(3, 'avg_score')
        df_bottom3 = df_health_scores.nsmallest(3, 'avg_score')

    score_sort_json = {
        "top3": json.loads(df_top3.to_json(orient='records', force_ascii=False)),
        "bottom3": json.loads(df_bottom3.to_json(orient='records', force_ascii=False)),
        "count": len(df_health_scores),
        "avg_overall": float(
            df_health_scores.select_dtypes(include=[np.number]).mean().mean()) if not df_health_scores.empty else 0
    }

    return dept_scores_json, group_scores_json, dept_scores_csv, group_scores_csv, score_sort_json


def _build_health_analysis_payload(
        request_payload: dict,
        df_summary: pd.DataFrame,
        df_health_scores: pd.DataFrame = None,
        weekly_records: list = None,
        dept_scores: list = None,
        group_scores: list = None,
        score_sort_json: dict = None
) -> dict:
    """
    将各项数据整理成统一的 WebSocket 输出格式。
    对标 equipment.py 的 _build_analysis_payload()

    参数:
        request_payload: 原始请求数据
        df_summary: 汇总统计表
        df_health_scores: 六维评分表
        weekly_records: 周度汇总列表
        dept_scores: 部门评分列表
        group_scores: 分组评分列表
        score_sort_json: 排序统计JSON

    返回:
        dict: 标准的WebSocket响应格式
    """
    payload = {
        "type": "analysis_data",
        "time_range": {
            "start": request_payload.get('start_time'),
            "end": request_payload.get('end_time')
        },
        "summary": json.loads(df_summary.to_json(orient='records',
                                                 force_ascii=False)) if df_summary is not None and not df_summary.empty else [],
        "health_scores": json.loads(df_health_scores.to_json(orient='records',
                                                             force_ascii=False)) if df_health_scores is not None and not df_health_scores.empty else [],
        "weekly_summary": weekly_records or [],
        "department_scores": dept_scores or [],
        "group_scores": group_scores or [],
        "score_sort": score_sort_json or {},
        "overall_avg": {
            "avg_hr_day": float(
                df_summary['avg_hr_day'].mean()) if df_summary is not None and not df_summary.empty else 0,
            "avg_spo2": float(df_summary['avg_spo2'].mean()) if df_summary is not None and not df_summary.empty else 0,
        }
    }

    return payload


async def detect_recovery_hr_and_fatigue(query_bool: dict,
                                         start_time: str,
                                         end_time: str,
                                         imeis: list | None = None,
                                         expected_drop_per_min: float = 12.0) -> pd.DataFrame:
    """
    按注释要求实现：
    - 在按时间顺序出现连续≥2条 intensity>2 的“运动段”后，第一条 intensity<2 的心率视为“运动恢复心率”。
    - 若数据中断（无后续记录），在最后一条运动记录之后的 2 分钟窗口内查找心率来近似恢复值。
    - 计算恢复疲劳指数（可调期望每分钟下降 expected_drop_per_min，默认 12 bpm/min）。

    返回：每个恢复事件一行，包括：
      imei, exercise_start_ts, exercise_end_ts, exercise_count,
      exercise_end_hr, recovery_ts, recovery_hr,
      delta_hr, delta_seconds, fatigue_index, method
    """
    # 使用分页方式获取恢复心率数据，避免单次查询过多数据
    df = await get_hr_timeseries_with_pagination(
        query_bool,
        start_time=start_time,
        end_time=end_time,
        imeis=imeis,
        batch_size=5000,
        process_func=stream_process_hr_timeseries
    )
    if df.empty:
        return pd.DataFrame()

    # 统一时间与数值类型
    df["ts"] = pd.to_datetime(df["createDate"], errors="coerce")
    df["hr"] = pd.to_numeric(df["hr"], errors="coerce")
    df["intensity"] = pd.to_numeric(df["intensity"], errors="coerce")
    df = df.dropna(subset=["imei", "ts"])  # imei 和时间不可缺失
    df = df.sort_values(["imei", "ts"]).reset_index(drop=True)

    results = []

    from datetime import timedelta

    for imei, sub in df.groupby("imei"):
        sub = sub.dropna(subset=["hr", "intensity", "ts"]).reset_index(drop=True)
        n = len(sub)
        i = 0
        while i < n:
            if sub.loc[i, "intensity"] >= 2:
                start_i = i
                count = 1
                j = i + 1
                while j < n and sub.loc[j, "intensity"] >= 2:
                    count += 1
                    j += 1

                if count >= 2:  # 满足连续2条或以上
                    exercise_end_hr = float(sub.loc[j - 1, "hr"])
                    exercise_end_ts = sub.loc[j - 1, "ts"]

                    recovery_hr = None
                    recovery_ts = None
                    method = "immediate"

                    # 情况1：紧随其后的记录为休息（intensity<=1）
                    if j < n and sub.loc[j, "intensity"] <= 1 and pd.notna(sub.loc[j, "hr"]):
                        recovery_hr = float(sub.loc[j, "hr"]) if pd.notna(sub.loc[j, "hr"]) else None
                        recovery_ts = sub.loc[j, "ts"]
                    else:
                        # 情况2：数据中断，向后2分钟窗口查询该 IMEI 的心率记录
                        lookup_start = exercise_end_ts
                        lookup_end = exercise_end_ts + timedelta(minutes=2)
                        add_rows = await get_hr_timeseries_by_imei(
                            query_bool,
                            start_time=lookup_start.isoformat(),
                            end_time=lookup_end.isoformat(),
                            imeis=[imei],
                            size=1000
                        )
                        if add_rows:
                            add_df = pd.DataFrame(add_rows)
                            add_df["ts"] = pd.to_datetime(add_df["createDate"], errors="coerce")
                            add_df["hr"] = pd.to_numeric(add_df["hr"], errors="coerce")
                            add_df["intensity"] = pd.to_numeric(add_df["intensity"], errors="coerce")
                            add_df = add_df.dropna(subset=["ts", "hr"]).sort_values("ts")
                            found = add_df[(add_df["intensity"].notna()) & (add_df["intensity"] <= 1)]
                            sel = found.iloc[0] if not found.empty else (add_df.iloc[0] if not add_df.empty else None)
                            if sel is not None:
                                recovery_hr = float(sel["hr"]) if pd.notna(sel["hr"]) else None
                                recovery_ts = sel["ts"]
                                method = "lookup_2min"

                    delta_hr = None
                    delta_seconds = None
                    fatigue_index = None
                    if recovery_hr is not None:
                        delta_hr = max(0.0, exercise_end_hr - recovery_hr)
                        if recovery_ts is not None:
                            delta_seconds = float((recovery_ts - exercise_end_ts).total_seconds())
                            expected_drop = expected_drop_per_min * (
                                        delta_seconds / 60.0) if delta_seconds > 0 else None
                            if expected_drop and expected_drop > 0:
                                efficiency = max(0.0, min(1.0, delta_hr / expected_drop))
                                fatigue_index = round((1.0 - efficiency) * 100.0, 1)

                    results.append({
                        "imei": imei,
                        "exercise_start_ts": sub.loc[start_i, "ts"],
                        "exercise_end_ts": exercise_end_ts,
                        "exercise_count": int(count),
                        "exercise_end_hr": round(exercise_end_hr, 2),
                        "recovery_ts": recovery_ts,
                        "recovery_hr": round(recovery_hr, 2) if recovery_hr is not None else None,
                        "delta_hr": round(delta_hr, 2) if delta_hr is not None else None,
                        "delta_seconds": round(delta_seconds, 1) if delta_seconds is not None else None,
                        "fatigue_index": fatigue_index,
                        "method": method
                    })

                i = j  # 跳到运动段之后继续搜索
            else:
                i += 1

    return pd.DataFrame(results)


def _classify_cs_level(cs: float) -> str:
    """将 CS 分数映射成等级标签"""
    if np.isnan(cs):
        return "N/A"
    if cs >= 85:
        return "A 优秀"
    elif cs >= 70:
        return "B 良好"
    elif cs >= 55:
        return "C 一般"
    else:
        return "D 警告"


def analyze_health_period(df: pd.DataFrame,
                          imei_col: str = "imei",
                          date_col: str = "date",
                          p20_col: str = "P20",
                          std_col: str = "std_p20_p80") -> pd.DataFrame:
    """
    对一个时间窗口内的多设备日级数据做健康分析。

    该函数通过分析设备的心率数据和波动数据，评估设备的健康状况和趋势。
    参数:
        df: 包含 imei / date / P20 / std_p20_p80 的 DataFrame，原始数据集
        imei_col: 设备字段名，默认为"imei"
        date_col: 日期字段名（可转为 datetime），默认为"date"
        P20_col: 日静息心率 P20 字段名，默认为"p20"
        std_col: P10~P80 区间标准差字段名，默认为"std_p20_p80"

    返回:
        result_df: 每个 imei 一行的结果 DataFrame，字段包括：
            imei
            start_date, end_date
            valid_days, period_span_days
            p20_mean, p20_std
            std_mean, std_std
            trend_P20, trend_std
            CS_month, CS_level
    """

    if df.empty:
        return pd.DataFrame()

    # 拷贝一份，避免改动原 df
    data = df.copy()

    # 重命名到内部统一字段
    data = data.rename(columns={
        imei_col: "imei",
        date_col: "date",
        p20_col: "P20",
        std_col: "std"
    })

    # 转时间 & 数值
    data["date"] = pd.to_datetime(data["date"], errors="coerce")
    data["p20"] = pd.to_numeric(data["p20"], errors="coerce")
    data["std"] = pd.to_numeric(data["std"], errors="coerce")

    # 有的天两个字段都是 None，这里视为缺失记录，后面按 imei 再过滤
    results = []

    for imei, sub in data.groupby("imei"):
        sub = sub.sort_values("date")

        # 只保留 P20 或 std 至少有一个不是 NaN 的天
        valid_mask = sub["p20"].notna() | sub["std"].notna()
        sub_valid = sub.loc[valid_mask].copy()

        if sub_valid.empty:
            # 这个 imei 在该时间窗口完全没有效数据
            results.append({
                "imei": imei,
                "start_date": sub["date"].min(),
                "end_date": sub["date"].max(),
                "valid_days": 0,
                "period_span_days": (sub["date"].max() - sub["date"].min()).days + 1
                if sub["date"].notna().any() else np.nan,
                "p20_mean": np.nan,
                "p20_std": np.nan,
                "std_mean": np.nan,
                "std_std": np.nan,
                "trend_p20": 0.0,
                "trend_std": 0.0,
                "CS_month": np.nan,
                "CS_level": "N/A",
            })
            continue

        # 基本时间跨度信息
        start_date = sub["date"].min()
        end_date = sub["date"].max()
        period_span_days = (end_date - start_date).days + 1
        valid_days = sub_valid["date"].nunique()

        # 统计量（按窗口整体算：这个窗口内每日 P20 / std 的均值 & 波动）
        P20_mean = float(sub_valid["p20"].mean())
        P20_std = float(sub_valid["p20"].std(ddof=1))  # 日静息心率在窗口内的波动

        std_mean = float(sub_valid["std"].mean())
        std_std = float(sub_valid["std"].std(ddof=1))  # 每日“波动”的再波动，反映状态稳定性

        # 计算趋势（以天数相对起点的偏移作为 x）
        x_days = (sub_valid["date"] - sub_valid["date"].min()).dt.days.to_numpy()
        y_P20 = sub_valid["p20"].to_numpy()
        y_std = sub_valid["std"].to_numpy()

        trend_P20 = _compute_trend(x_days, y_P20)
        trend_std = _compute_trend(x_days, y_std)

        # 计算综合评分 & 等级
        cs_month = _compute_cs(P20_mean, std_mean, std_std, trend_P20)
        cs_level = _classify_cs_level(cs_month)

        results.append({
            "imei": imei,
            "start_date": start_date.date() if pd.notna(start_date) else np.nan,
            "end_date": end_date.date() if pd.notna(end_date) else np.nan,
            "valid_days": int(valid_days),
            "period_span_days": int(period_span_days),
            "p20_mean": round(P20_mean, 2) if not np.isnan(P20_mean) else np.nan,
            "p20_std": round(P20_std, 2) if not np.isnan(P20_std) else np.nan,
            "std_mean": round(std_mean, 2) if not np.isnan(std_mean) else np.nan,
            "std_std": round(std_std, 2) if not np.isnan(std_std) else np.nan,
            "trend_p20": round(trend_P20, 4),
            "trend_std": round(trend_std, 4),
            "CS_month": round(cs_month, 1) if not np.isnan(cs_month) else np.nan,
            "CS_level": cs_level,
        })

    result_df = pd.DataFrame(results)
    return result_df


# ====================================================================
# AI分析提示词生成（阶段5）
# ====================================================================

def calculate_oxygen_efficiency(df: pd.DataFrame) -> pd.DataFrame:
    """
    第2维：供氧效率 (OE - Oxygen Efficiency)

    基于血氧饱和度的稳定性和低氧事件频率。
    理想血氧：96%以上，低于95%的时间越少越好。

    参数:
        df: 包含 imei, date, avg_spo2, spo2_ratio_lt95 的DataFrame

    返回:
        包含 imei, oxygen_efficiency, oe_level 的DataFrame
    """
    if df.empty:
        return pd.DataFrame()

    # 基于平均血氧的评分
    # 96%为理想值，每低1%扣10分
    spo2_penalty = 10 * (96 - df['avg_spo2']).fillna(0).clip(lower=0)

    # 低氧事件惩罚
    # 低于95%的比例每1%扣5分
    low_o2_penalty = 5 * df['spo2_ratio_lt95'].fillna(0)

    # 综合评分
    oe_score = 100 - spo2_penalty - low_o2_penalty
    oe_score = oe_score.clip(0, 100).round(2)

    result = pd.DataFrame({
        'imei': df['imei'],
        'oxygen_efficiency': oe_score,
        'oe_level': oe_score.apply(lambda x: _classify_health_level(x))
    })

    return result


def calculate_circadian_rhythm(df: pd.DataFrame) -> pd.DataFrame:
    """
    第6维：昼夜节律稳定性 (CS2 - Circadian Stability)

    健康的昼夜节律：白天心率略高于夜间（比值 1.1~1.3）
    - <1.0：昼夜颠倒，健康风险极高
    - 1.0~1.1：昼夜差异不足，交感神经欠缺
    - 1.1~1.3：健康范围
    - >1.3：白天过度劳累，压力大

    参数:
        df: 包含 imei, date, hr_day_night_ratio 的DataFrame

    返回:
        包含 imei, circadian_rhythm, cs2_level 的DataFrame
    """
    if df.empty:
        return pd.DataFrame()

    ideal_ratio = 1.2
    tolerance = 0.15

    # 昼夜比在理想范围内得满分
    cs2_score = np.where(
        df['hr_day_night_ratio'].between(ideal_ratio - tolerance, ideal_ratio + tolerance),
        100,
        100 - 50 * np.abs(df['hr_day_night_ratio'] - ideal_ratio)
    )
    cs2_score = np.clip(cs2_score, 0, 100).round(2)

    result = pd.DataFrame({
        'imei': df['imei'],
        'circadian_rhythm': cs2_score,
        'cs2_level': pd.Series(cs2_score).apply(lambda x: _classify_health_level(x))
    })

    return result


def calculate_physical_fitness(df_recovery: pd.DataFrame) -> pd.DataFrame:
    """
    第4维：恢复与疲劳指数 (RF - Recovery & Fatigue)

    基于运动后心率恢复速度和夜间心率。
    - 恢复快（疲劳指数低）：体能好，恢复能力强
    - 恢复慢（疲劳指数高）：体能差，疲劳积累

    参数:
        df_recovery: 运动恢复分析结果，包含 imei, fatigue_index

    返回:
        包含 imei, physical_fitness, rf_level 的DataFrame
    """
    if df_recovery.empty:
        return pd.DataFrame(columns=['imei', 'physical_fitness', 'rf_level'])

    # 从疲劳指数反推体能评分
    # 疲劳指数越高，体能评分越低
    # rf_score = 100 - fatigue_index
    # 按imei聚合：取平均疲劳指数

    df_agg = df_recovery.groupby('imei').agg({
        'fatigue_index': 'mean'
    }).reset_index()

    rf_score = 100 - df_agg['fatigue_index'].fillna(0)
    rf_score = rf_score.clip(0, 100).round(2)

    result = pd.DataFrame({
        'imei': df_agg['imei'],
        'physical_fitness': rf_score,
        'rf_level': rf_score.apply(lambda x: _classify_health_level(x))
    })

    return result


def calculate_stress_index(df_hr_timeseries: pd.DataFrame) -> pd.DataFrame:
    """
    第5维：心理压力指数 (PS - Psycho Stress)

    利用静息状态下心率变化率的波动作为HRV的proxy。
    心率波动剧烈 => 自主神经系统紧张 => 压力大。

    参数:
        df_hr_timeseries: 心率时序数据，包含 imei, ts, hr, intensity

    返回:
        包含 imei, stress_index, ps_level 的DataFrame
    """
    if df_hr_timeseries.empty:
        return pd.DataFrame(columns=['imei', 'stress_index', 'ps_level'])

    df = df_hr_timeseries.copy()
    df['ts'] = pd.to_datetime(df['ts'], errors='coerce')
    df['hr'] = pd.to_numeric(df['hr'], errors='coerce')
    df['intensity'] = pd.to_numeric(df['intensity'], errors='coerce')
    df = df.dropna(subset=['imei', 'ts', 'hr', 'intensity'])
    df = df.sort_values(['imei', 'ts'])

    results = []
    for imei, group in df.groupby('imei'):
        # 只取静息状态（intensity < 2）的数据
        rest_data = group[group['intensity'] < 2].copy()

        if len(rest_data) < 3:
            # 数据不足，跳过
            continue

        rest_data = rest_data.sort_values('ts').reset_index(drop=True)

        # 计算心率变化率 dHR/dt
        hr_values = rest_data['hr'].values
        time_diffs = rest_data['ts'].diff().dt.total_seconds().values

        # 避免时间差为0
        time_diffs = np.where(time_diffs > 0, time_diffs, np.nan)
        hr_diffs = np.diff(hr_values, prepend=np.nan)

        # dHR/dt
        hr_change_rate = np.abs(hr_diffs / time_diffs)
        hr_change_rate = hr_change_rate[~np.isnan(hr_change_rate)]

        if len(hr_change_rate) < 2:
            continue

        # 心率变化率的标准差（波动程度）
        hr_volatility = np.std(hr_change_rate)

        # 压力评分：波动越大压力越高
        # 正常静息波动范围：0.1~0.3 bpm/s
        # 如果波动>0.5 bpm/s 说明压力大
        ps_score = 100 - min(100, hr_volatility * 100)
        ps_score = max(0, ps_score)

        results.append({
            'imei': imei,
            'stress_index': round(ps_score, 2),
            'hr_volatility': round(hr_volatility, 4)
        })

    if not results:
        return pd.DataFrame(columns=['imei', 'stress_index', 'ps_level'])

    result_df = pd.DataFrame(results)
    result_df['ps_level'] = result_df['stress_index'].apply(lambda x: _classify_health_level(x))

    return result_df[['imei', 'stress_index', 'ps_level']]


def calculate_metabolic_efficiency(df_activity: pd.DataFrame) -> pd.DataFrame:
    """
    第3维：代谢运动效率 (ME - Metabolic Efficiency)

    基于运动时心率与步频的匹配程度。
    斜率越小（同样步频下心率上升幅度越小）表示有氧代谢效率越高。

    参数:
        df_activity: 包含 imei, date, intensity, step_delta, hr 的DataFrame
                     或运动恢复数据

    返回:
        包含 imei, metabolic_efficiency, me_level 的DataFrame
    """
    if df_activity.empty:
        return pd.DataFrame(columns=['imei', 'metabolic_efficiency', 'me_level'])

    # 对于初期版本，我们用疲劳指数的反向来表示代谢效率
    # (更精确的计算需要步数增量数据)
    # 假设高效率的人运动后恢复快

    # 这里简单实现：根据运动强度和恢复能力推断
    # TODO: 当有详细的步数-心率数据时，实现完整的斜率计算

    # 临时实现：返回默认值（75分）
    result = pd.DataFrame({
        'imei': df_activity.get('imei', pd.Series(dtype=object)),
        'metabolic_efficiency': 75.0,
        'me_level': '良好'
    })

    return result


def calculate_overall_health_score(
        cs_scores: pd.DataFrame = None,
        oe_scores: pd.DataFrame = None,
        me_scores: pd.DataFrame = None,
        rf_scores: pd.DataFrame = None,
        ps_scores: pd.DataFrame = None,
        cs2_scores: pd.DataFrame = None,
        weights: dict = None
) -> pd.DataFrame:
    """
    综合六维健康评分，生成总体健康分数。

    参数:
        cs_scores: 心血管稳定性评分
        oe_scores: 供氧效率评分
        me_scores: 代谢效率评分
        rf_scores: 恢复与疲劳评分
        ps_scores: 心理压力评分
        cs2_scores: 昼夜节律评分
        weights: 权重字典 {'cs': 0.2, 'oe': 0.15, ...}

    返回:
        包含六维分数和综合分的DataFrame
    """
    if weights is None:
        weights = {
            'cs': 0.20,  # 心血管稳定性
            'oe': 0.15,  # 供氧效率
            'me': 0.15,  # 代谢效率
            'rf': 0.20,  # 恢复与疲劳
            'ps': 0.15,  # 心理压力
            'cs2': 0.15  # 昼夜节律
        }

    # TODO: 实现完整的综合评分逻辑
    # 这里是占位符，后续完成各评分函数后填充

    return pd.DataFrame()


# ====================================================================
# 大数据处理优化函数（ES数据量大的处理策略）
# ====================================================================

async def get_hr_timeseries_with_pagination(
        query_bool: dict,
        start_time: str,
        end_time: str,
        imeis: list = None,
        batch_size: int = 5000,
        process_func=None
) -> pd.DataFrame:
    """
    分页获取心率时序数据，避免一次性加载过多数据。
    对标 detect_recovery_hr_and_fatigue 中的大数据处理。

    参数:
        query_bool: ES查询条件
        start_time: 开始时间
        end_time: 结束时间
        imeis: 设备ID列表
        batch_size: 每批次获取的数据数量（默认5000，可根据内存调整）
        process_func: 可选的数据处理函数，在每批次数据中调用

    返回:
        pd.DataFrame: 合并后的所有数据

    优点：
        ✅ 分批次加载，避免内存溢出
        ✅ 支持流式处理
        ✅ 可在加载过程中应用过滤逻辑
    """
    all_data = []
    total_fetched = 0
    search_after_token = None

    while True:
        # 使用 search_after 深分页，避免 result_window 限制
        try:
            page = await get_hr_timeseries_by_imei_page(
                query_bool,
                start_time=start_time,
                end_time=end_time,
                imeis=imeis,
                size=batch_size,
                search_after=search_after_token
            )
            batch_data = page.get("rows", [])
            next_token = page.get("last_sort")
        except Exception as e:
            print(f"⚠️  ES查询失败（search_after={search_after_token}）: {str(e)}")
            break

        # 检查是否有数据
        if not batch_data:
            print(f"✅ 数据加载完成，共获取 {total_fetched} 条记录")
            break

        # 处理这批数据
        if process_func:
            batch_data = process_func(batch_data)

        all_data.extend(batch_data)
        total_fetched += len(batch_data)
        search_after_token = next_token

        print(f"📊 已加载 {total_fetched} 条记录...")

        # 若返回数量不足 batch_size 或无下一页标记，则结束
        if len(batch_data) < batch_size or not search_after_token:
            break

    return pd.DataFrame(all_data) if all_data else pd.DataFrame()


def stream_process_hr_timeseries(
        rows: list,
        chunk_size: int = 1000
) -> list:
    """
    流式处理心率时序数据，逐块处理而不是一次性加载全部。

    参数:
        rows: 原始数据列表
        chunk_size: 每块的大小

    返回:
        list: 处理后的数据

    使用场景：
        当需要对大量数据进行计算时，可以分块处理以节省内存
    """
    processed_data = []

    for i in range(0, len(rows), chunk_size):
        chunk = rows[i:i + chunk_size]
        df_chunk = pd.DataFrame(chunk)

        # 对这块数据进行处理（转换类型、清洗数据等）
        df_chunk['ts'] = pd.to_datetime(df_chunk['createDate'], errors='coerce')
        df_chunk['hr'] = pd.to_numeric(df_chunk['hr'], errors='coerce')
        df_chunk['intensity'] = pd.to_numeric(df_chunk['intensity'], errors='coerce')
        df_chunk = df_chunk.dropna(subset=['imei', 'ts'])

        processed_data.extend(df_chunk.to_dict('records'))

    return processed_data


async def get_resting_hr_with_sampling(
        query_bool: dict,
        start_time: str,
        end_time: str,
        sampling_rate: float = 0.1
) -> pd.DataFrame:
    """
    获取日级静息心率数据时进行采样，减少数据量。

    参数:
        query_bool: ES查询条件
        start_time: 开始时间
        end_time: 结束时间
        sampling_rate: 采样率（0.0-1.0），0.1表示取10%的数据

    返回:
        pd.DataFrame: 采样后的数据

    使用场景：
        当数据量太大但不需要100%精度时，可以通过采样快速得到统计结果
        （均值、标准差等的统计量基本不会因为采样而改变太多）
    """
    # 获取全量数据
    health_data = await get_resting_hr_P20_by_imei_day(
        query_bool,
        start_time=start_time,
        end_time=end_time
    )

    if not health_data:
        return pd.DataFrame()

    df = pd.DataFrame(health_data)

    # 按采样率随机选择数据
    if sampling_rate < 1.0:
        sample_size = max(1, int(len(df) * sampling_rate))
        df = df.sample(n=sample_size, random_state=42)
        print(f"📊 采样率: {sampling_rate * 100:.1f}%, 原数据: {len(health_data)}, 采样后: {len(df)}")

    return df


def optimize_dataframe_memory(df: pd.DataFrame) -> pd.DataFrame:
    """
    优化DataFrame的内存使用，降低数据类型精度。

    参数:
        df: 输入DataFrame

    返回:
        pd.DataFrame: 内存优化后的DataFrame

    优化策略：
        - int64 → int32 (若范围允许)
        - float64 → float32
        - 删除不必要的列

    效果：
        通常可以减少40-50%的内存占用
    """
    df_optimized = df.copy()

    for col in df_optimized.columns:
        col_type = df_optimized[col].dtype

        # 整数优化
        if col_type == 'int64':
            min_val = df_optimized[col].min()
            max_val = df_optimized[col].max()
            if min_val > -2147483648 and max_val < 2147483647:
                df_optimized[col] = df_optimized[col].astype('int32')

        # 浮点数优化
        elif col_type == 'float64':
            df_optimized[col] = df_optimized[col].astype('float32')

    # 计算内存使用量
    original_memory = df.memory_usage(deep=True).sum() / 1024 / 1024
    optimized_memory = df_optimized.memory_usage(deep=True).sum() / 1024 / 1024
    reduction = (1 - optimized_memory / original_memory) * 100

    print(f"💾 内存优化: {original_memory:.2f}MB → {optimized_memory:.2f}MB (减少 {reduction:.1f}%)")

    return df_optimized


def create_incremental_update_query(
        start_time: str,
        end_time: str,
        last_update_time: str = None
) -> dict:
    """
    创建增量更新查询，只获取上次更新后的新数据。

    参数:
        start_time: 统计周期开始时间
        end_time: 统计周期结束时间
        last_update_time: 上次更新的时间（若为None则全量查询）

    返回:
        dict: ES查询条件

    使用场景：
        如果每天都要分析数据，可以只查询新增数据而不是重新查询所有历史数据
        这样可以大幅减少查询量和处理时间
    """
    if last_update_time:
        # 增量查询：只查询update_time > last_update_time的数据
        query = {
            "bool": {
                "must": [
                    {"range": {"createDate": {"gte": last_update_time, "lte": end_time}}}
                ]
            }
        }
        print(f"📈 增量查询: 仅查询 {last_update_time} 之后的数据")
    else:
        # 全量查询
        query = {
            "bool": {
                "must": [
                    {"range": {"createDate": {"gte": start_time, "lte": end_time}}}
                ]
            }
        }
        print(f"📊 全量查询: {start_time} 到 {end_time}")

    return query


def _classify_health_level(score: float) -> str:
    """
    将健康评分（0-100）映射到等级标签

    参数:
        score: 0-100的分数

    返回:
        等级标签
    """
    if np.isnan(score):
        return "N/A"
    if score >= 85:
        return "A 优秀"
    elif score >= 70:
        return "B 良好"
    elif score >= 55:
        return "C 一般"
    else:
        return "D 需改进"


# ====================================================================
# AI分析提示词生成（阶段5）
# ====================================================================

def gen_health_query(param: dict = None) -> str:
    """
    构建健康分析AI查询的提示词。
    对标 equipment.py 的 gen_query()

    参数:
        param: 包含以下数据的字典
            - summary: 汇总统计数据 (CSV格式)
            - health_scores: 六维评分数据 (CSV格式)
            - weekly_records: 周度健康统计 (CSV格式)
            - recovery_data: 运动恢复分析 (CSV格式)

    返回:
        str: AI提示词
    """
    query = (
        "以下是员工健康数据分析所依据的计算后的数据\n\n"

        "## A. 员工健康汇总统计数据\n"
        f"{param.get('summary', '')}\n\n"

        "## B. 六维健康评分（心血管稳定性/供氧效率/代谢效率/恢复疲劳/心理压力/昼夜节律）\n"
        f"{param.get('health_scores', '')}\n\n"

        "## C. 周度健康趋势统计\n"
        f"{param.get('weekly_records', '')}\n\n"

        # "## D. 运动恢复与疲劳指数分析\n"
        # f"{param.get('recovery_data', '')}\n\n"

        "---\n\n"

        "## 业务背景与关键定义\n\n"

        "### 六维评分系统说明\n"
        "- **CS（心血管稳定性）**: 基于静息心率+波动。低于60bpm理想，波动小说明稳定。评分越高越健康。\n"
        "- **OE（供氧效率）**: 基于血氧饱和度。理想≥95%，长期<95%示意肺功能或循环问题。\n"
        "- **ME（代谢效率）**: 运动时心率与负荷的比值。高效率=同样运动心率上升幅度小。\n"
        "- **RF（恢复疲劳）**: 基于运动后心率恢复速度。恢复快=体能好；恢复慢=疲劳积累。\n"
        "- **PS（心理压力）**: 基于静息心率波动。波动大=自主神经亢奋=压力大。\n"
        "- **CS2（昼夜节律）**: 白天/夜间心率比值。理想1.1~1.3，<1=昼夜颠倒，>1.3=白天过度劳累。\n\n"

        "### 等级定义\n"
        "- **A（优秀）**: 85-100分。各维健康指标均衡，建议维持当前生活方式。\n"
        "- **B（良好）**: 70-84分。轻度异常，建议关注特定维度，做预防性调整。\n"
        "- **C（一般）**: 55-69分。中度异常，需要干预。建议调整作息/减少压力。\n"
        "- **D（需改进）**: <55分。严重异常，建议医学干预或休息调理。\n\n"

        "---\n\n"

        "## 分析任务（请严格按步骤进行）\n\n"

        "### 1. 总体盘点\n"
        "- 给出员工总数、健康等级分布（A/B/C/D比例）\n"
        "- 综合评分的分位分布（P10/P20/P80）\n"
        "- 关键指标的均值（各维评分均值、平均心率、平均血氧）\n\n"

        "### 2. 问题清单（Top N）\n"
        "**维度**: 按个人、部门两个维度统计\n"
        "**规则（从高到低优先）**：\n"
        "  1. 综合分低 & 压力指数高 → 高风险人群\n"
        "  2. CS2昼夜节律严重紊乱(<1.0 or >1.5) → 作息问题\n"
        "  3. RF疲劳指数高(>70) & 恢复能力差 → 体能透支\n"
        "  4. OE供氧效率低(<65) & 低氧事件频繁 → 心肺问题\n"
        "  5. PS压力指数高 + CS心率不稳定 → 心理压力大\n\n"

        "### 3. 可能原因分析（证据导向）\n"
        "用具体字段和数据值作为证据，例如：\n"
        "  - 昼夜节律紊乱 & 夜间心率不降 ⇒ 夜班或熬夜\n"
        "  - 疲劳指数高 & 运动恢复慢 & 总工作时长超标 ⇒ 体能透支\n"
        "  - 心率波动大 & 低氧事件 & 压力高 ⇒ 高压力+缺氧环境\n\n"

        "### 4. 健康干预建议\n"
        "**按优先级列出可执行的建议**：\n"
        "  - 对象：指定人员或部门\n"
        "  - 问题：具体的健康指标异常\n"
        "  - 建议：具体可行的调整（如调整班次、增加休息、改善环境）\n"
        "  - 目标：量化的改善目标（如将CS2恢复到1.1~1.3区间）\n"
        "  - 时限：建议实施的时间限制\n\n"

        "### 5. 下周跟踪清单\n"
        "列出需要重点关注的员工/指标，及观察周期。\n\n"

        "### 6. 数据质量说明\n"
        "指出数据缺失、异常、或样本不足的情况。\n\n"

        "---\n\n"

        "## 输出格式要求\n\n"

        "**必须使用Markdown格式**（带# ## ### 标题）\n"
        "**使用中文输出**；数字保留两位小数；引用具体员工ID/部门/周期作为证据\n"
        "**结构如下（一级标题固定）**：\n\n"

        "# 员工健康分析报告\n\n"
        "## 一、管理摘要\n"
        "（3-5条关键发现，便于高管快速了解）\n\n"

        "## 二、总体盘点\n"
        "（员工数、等级分布、核心指标均值）\n\n"

        "## 三、问题清单\n"
        "（Top N问题，含具体证据和影响）\n\n"

        "## 四、原因分析\n"
        "（根据数据关联性推断可能原因）\n\n"

        "## 五、干预建议\n"
        "（具体、可执行、有时限和目标）\n\n"

        "## 六、下周跟踪清单\n"
        "（重点员工和观察指标）\n\n"

        "## 七、数据质量与边界\n"
        "（说明限制条件和数据缺陷）\n\n"

        "---\n\n"

        "请严格遵循上述格式和分析流程，不要输出你的推理过程。缺少的数据可以先不用分析。但是结构里需要有。可以说数据暂时未充足什么的"
    )
    return query
